using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;

using System.Runtime.CompilerServices;

namespace Bots.Index
{
    public class IndexTasker : MutexTask
    {
        protected Hashtable dirs = null;
        
        private class IndexReaderWrapper {
            private Hashtable hash = null;
            public IndexReaderWrapper(){
                hash = new Hashtable();
            }

            public IndexReader Open(Directory dir)
            {
                IndexReader reader = null;
                if (hash.Contains(dir))
                {
                    reader = hash[dir] as IndexReader;
                }
                else {
                    reader = IndexReader.Open(dir);
                    hash.Add(dir, reader);
                }
                return reader;
            }

            public bool IsOpen(Directory dir) {
                return hash.Contains(dir);
            }

            public void Close() {
                IEnumerator e = hash.Values.GetEnumerator();
                if (e.MoveNext()) {
                    IndexReader reader = e.Current as IndexReader;
                    reader.Close();
                }
            }
        }

        private class IndexWriteWrapper
        {
            private Hashtable hash = null;
            public IndexWriteWrapper()
            {
                hash = new Hashtable();
            }
            public IndexWriter Open(Directory dir)
            {
                IndexWriter writer = null;
                if (hash.Contains(dir))
                {
                    writer = hash[dir] as IndexWriter;
                }
                else
                {
                    bool create = (!IndexReader.IndexExists(dir));
                    writer = new IndexWriter(dir, new StandardAnalyzer(), create);
                    //Controls segment merge frequency and size
                    writer.SetMergeFactor(100);
                    //Limits the number of documents per segement
                    //writer.maxMergeDocs = 999999;
                    // Controls the amount of RAM used when indexing
                    writer.SetMaxMergeDocs(100);
                    hash.Add(dir, writer);
                }
                return writer;
            }

            public bool IsOpen(Directory dir)
            {
                return hash.Contains(dir);
            }

            public void Close()
            {
                IEnumerator e = hash.Values.GetEnumerator();
                if (e.MoveNext())
                {
                    IndexWriter writer = e.Current as IndexWriter;
                    writer.Close();
                }
            }
        }

        public IndexTasker()
        {
            //this.rebuildIndex = SearchConfig.RebuildIndex;
            dirs = new Hashtable();
        }
     
        protected Directory GetDirectory(DomainObject domainObject) {
            Directory currentDir = null;
            string key = domainObject.GetType().Name;
            if (dirs.Contains(key))
            {
                currentDir = dirs[key] as Directory;
            }
            else {
                currentDir = FSDirectory.GetDirectory(SearchConfig.GetDatabaseLocation(domainObject.GetType().Name), false);
                dirs.Add(key, currentDir);
            }
            return currentDir;
        }

        protected void BathAddDocument(Queue<DomainObject> queue)
        {
            if (queue.Count == 0) return;
            SearchConfig.Count += queue.Count;
            logger.Info("bathAddDocument:" + queue.Count);
            if (queue.Count == 1)
            {
                AddDocument(queue.Dequeue());
                return;
            }

            IndexWriteWrapper wrapper = new IndexWriteWrapper();
            while(queue.Count != 0)
            {
                DomainObject obj = queue.Dequeue();
                Directory indexDirectory = GetDirectory(obj);
                IndexWriter writer = wrapper.Open(indexDirectory);
                Document doc = ConvertorRegistry.Instance.Find(obj).Convert(obj);
                writer.AddDocument(doc);
            }
            wrapper.Close();
            SearchConfig.IndexOptimize = true;
        }

        protected void AddDocument(DomainObject domainObject)
        {
            Directory indexDirectory = GetDirectory(domainObject);
            bool create = (!IndexReader.IndexExists(indexDirectory));
            IndexWriter indexWriter = new IndexWriter(indexDirectory, new StandardAnalyzer(), create);
            Document doc = ConvertorRegistry.Instance.Find(domainObject).Convert(domainObject);
            indexWriter.AddDocument(doc);
            indexWriter.Close();
            SearchConfig.IndexOptimize = true;
            logger.Info("Add document" + domainObject.GetType().Name + ":" + domainObject.ID.ToString());
        }

        protected void BathDeleteDocument(Queue<DomainObject> queue)
        {
            if (queue.Count == 0) return;
            SearchConfig.Count += queue.Count;
            logger.Info("BathDeleteDocument:" + queue.Count);
            if (queue.Count == 1)
            {
                DeleteDocument(queue.Dequeue());
                return;
            }

            IndexReaderWrapper wrapper = new IndexReaderWrapper();
            while (queue.Count != 0)
            {
                DomainObject obj = queue.Dequeue();
                Directory indexDirectory = GetDirectory(obj);
                IndexReader rdr = wrapper.Open(indexDirectory);
                Term term = new Term("ID", obj.ID.ToString());
                //set delete statue 
                rdr.DeleteDocuments(term);
            }
            wrapper.Close();
            SearchConfig.IndexOptimize = true;
        }

        protected void DeleteDocument(DomainObject domainObject) {
            Directory indexDirectory = GetDirectory(domainObject);
            IndexReader rdr = IndexReader.Open(indexDirectory);
            Term term = new Term("ID", domainObject.ID.ToString());
            rdr.DeleteDocuments(term);
            rdr.Close();
            SearchConfig.IndexOptimize = true;
            logger.Info("delete document" + domainObject.GetType().Name + ":" + domainObject.ID.ToString());
        }

        [MethodImpl( MethodImplOptions.Synchronized)]
        protected void BathUpdateDocument(Queue<DomainObject> queue)
        {
            Queue<DomainObject> localQ = new Queue<DomainObject>(queue);
            BathDeleteDocument(queue);
            BathAddDocument(localQ);
        }
      
    }
}
